//创建两个画布
const canvas1 = document.createElement('canvas'); //展示图
const canvas2 = document.createElement('canvas'); //绘图
canvas1.width = canvas2.width = 800 ;
canvas1.height = canvas2.height = 400 ;
const container = document.querySelector('#container') ;
container.append(canvas1);
container.append(canvas2);

const ctx1 = canvas1.getContext('2d');
const ctx2 = canvas2.getContext('2d') ;
let shapeType = 'line' ; //line , rect , circle , fill , rubber
let color = '#000000' ;
let lineWidth = 1 ;

function setStrokeStyle(ctx){
    ctx.strokeStyle = color ;
    ctx.lineWidth = lineWidth ;
}

//在操作图像像素时，根据坐标点，获得其r通道值的索引。 r+0,g+1,b+2,a+3
function point2Index(imageData,x,y){
    return (imageData.width * 4) * y + x * 4;
}

//将颜色的16进制表示转换成rgba表示
function hex2rgb(color){
    //#aabbcc - aabbcc - ["aa" , "bb" , "cc"]
    color = color.replace('#','');
    const r = eval('0x'+color.substring(0,2)) ;
    const g = eval('0x'+color.substring(2,4)) ;
    const b = eval('0x'+color.substring(4,6)) ;
    return [r,g,b] ;
}

//准备按钮事件
function preBtnEvent(){

   document.querySelectorAll('.shapeType').forEach((btn,i)=>{
        btn.onclick = function(){
            if(this.classList.contains('active')){
                return ;
            }
            //切换active
            document.querySelector('.active').classList.remove('active') ;
            this.classList.add('active');
            
            //切换图形绘制类型
            shapeType = this.getAttribute('data') ;
        }
   });
    
   document.querySelector('#boldBtn').onchange = function(){
        lineWidth = this.value ;
   }

   document.querySelector('#colorBtn').onchange = function(){
        color = this.value ;
   }

   document.querySelector('#clearBtn').onclick = function(){
        ctx1.clearRect(0,0,canvas1.width,canvas1.height) ;
   }

   document.querySelector('#saveBtn').onclick = function(){
        const url = canvas1.toDataURL();
        const a = document.createElement('a') ;
        a.href = url ;
        a.download = '保存图片' ;
        a.click();
   }
}
preBtnEvent();


//定义图形类
class Shape{
    constructor(type,x,y,ctx){
        this.type = type ;
        this.x = x ;
        this.y = y ;
        this.ctx = ctx ;
        this.ex = x ;
        this.ey = y ;
        this.points = [] ;
    }
    draw(){
        switch(this.type){
            case 'line' : this.drawLine(); break ;
            case 'rect' : this.drawRect(); break ;
            case 'circle' : this.drawCircle(); break ;
            case 'rubber' : this.drawRubber(); break ;
            case 'fill' : this.drawFill(); break ;
        }
    }

    drawLine(){
        this.ctx.beginPath();
        this.ctx.save();
        setStrokeStyle(this.ctx);
        this.ctx.moveTo(this.x,this.y);
        this.points.forEach((point,i)=>{
            this.ctx.lineTo(point.x,point.y);
        });
        this.ctx.stroke();
        this.ctx.restore();
    }
    drawRect(){
        this.ctx.beginPath();
        this.ctx.save();
        setStrokeStyle(this.ctx) ;
        //找到矩形左上角的点（最小的x和y）
        const x = Math.min(this.x , this.ex) ;
        const y = Math.min(this.y , this.ey) ;
        //计算矩形宽高
        const w = Math.abs(this.x - this.ex) ;
        const h = Math.abs(this.y - this.ey) ;
        this.ctx.strokeRect(x,y,w,h);
        this.ctx.restore();
    }
    drawCircle(){
        this.ctx.beginPath();
        this.ctx.save();
        setStrokeStyle(this.ctx) ;
        //两个点横纵坐标的一般即为x轴半径 和 y轴半径。
        const r1 = Math.abs(this.x - this.ex) / 2 ;
        const r2 = Math.abs(this.y - this.ey) / 2 ;
        //找到最小的点， 分别加上各自轴的半径，即为圆心点。
        const x = Math.min(this.x , this.ex) + r1 ;
        const y = Math.min(this.y, this.ey) + r2 ;
        if(r1 == r2){
            //正圆
            this.ctx.arc(x,y,r1,0,Math.PI*2);
        }else{
            //椭圆
            this.ctx.ellipse(x,y,r1,r2,0,0,Math.PI*2) ;
        }
        this.ctx.stroke();
        this.ctx.restore();
    }
    drawRubber(){
        this.ctx.beginPath();
        this.ctx.save();
        this.ctx.globalCompositeOperation = "destination-out";
        setStrokeStyle(this.ctx);
        this.ctx.moveTo(this.x,this.y);
        this.points.forEach((point,i)=>{
            this.ctx.lineTo(point.x,point.y);
        });
        this.ctx.stroke();
        this.ctx.restore();
    }
    drawFill(){
        //获得点击那个点rgba值
        //1个像素，有4个颜色值（rgba)
        const baseImageData = this.ctx.getImageData(this.x,this.y,1,1);
        const imageData = this.ctx.getImageData(0,0,canvas1.width,canvas1.height);


        const [r,g,b] = hex2rgb(color) ;
        function change(x,y){
            const stack = [[x,y]] ;
            //循环处理stack中的每一个点，判断是否需要填充
            while(stack.length > 0){
                //本次循环，处理一个点
                const[x,y] = stack.shift();
                if(x < 0 || y < 0 || x > canvas1.width || y > canvas1.height){
                    continue ;
                }
                //代码至此，说明这个点存在。判断这点是否需要填充
                const i = point2Index(imageData,x,y);
                if(baseImageData.data[0] == imageData.data[i] 
                    && baseImageData.data[1] == imageData.data[i+1]
                    && baseImageData.data[2] == imageData.data[i+2]
                    && baseImageData.data[3] == imageData.data[i+3]){
                      //相等， 这个位置颜色可以改变
                      imageData.data[i] = r ;
                      imageData.data[i+1] = g ;
                      imageData.data[i+2] = b ;
                      imageData.data[i+3] = 255 ;
      
                      //继续发散，再检查其四周
                      stack.push([x,y+1],[x,y-1],[x+1,y],[x-1,y]) ;
                  }else{
                      //不相等，到达边界
                      continue ;
                  }
            }

        }
        change(this.x,this.y);

        this.ctx.putImageData(imageData,0,0);

    }
}


//开始绘制
//  在画布中，按下鼠标时，开始绘制动作
const area = canvas2.getBoundingClientRect();
canvas2.onmousedown = function(e){
    let x = e.clientX - area.left ;
    let y = e.clientY - area.top ;
    let shape = new Shape(shapeType,x , y , ctx2);

    if(shapeType == 'fill'){
        //填充就不需要鼠标移动,直接根据点击的位置，在画布1上填充
        shape.ctx = ctx1 ;
        shape.draw();
    }else{
        if(shapeType == 'rubber'){
            ctx1.save();
            setStrokeStyle(ctx1);
            ctx1.beginPath();
            ctx1.moveTo(x,y);
        }else{
            ctx2.save();
            setStrokeStyle(ctx2);
            ctx2.beginPath();
            ctx2.moveTo(x,y)
        }

        //随着鼠标移动，开始绘制图形
        window.onmousemove = function(e){
            let ex = e.clientX - area.left ;
            let ey = e.clientY - area.top ; 

            switch(shapeType){
                case 'line':
                    ctx2.lineTo(ex,ey);
                    ctx2.stroke();
                    //记录点，等在画布2绘制完毕后，一次性将图形绘制到画布1上
                    shape.points.push({x:ex,y:ey});
                break ;
                case 'rect':
                case 'circle':
                    //先将图形绘制到画布2，结束的时候，在修改ctx=ctx1,将图形绘制到画布1上
                    shape.ex = ex ;
                    shape.ey = ey ;   
                    ctx2.clearRect(0,0,canvas2.width , canvas2.height);
                    shape.draw();  
                break ;
                case 'rubber':
                    ctx1.globalCompositeOperation = "destination-out";
                    ctx1.lineTo(ex,ey);
                    ctx1.stroke();
                    //尽管直接绘制在画布1上，但也要保留绘制的路径，确保未来画布1重绘效果。
                    shape.points.push({x:ex,y:ey});    
                break ;
            }
        }

        //鼠标代替，终止绘制动作，图形确定
        //清空画布2，将图形在画布1中重绘 （线条，矩形，圆形）
        window.onmouseup = function(){
            window.onmousemove = null ;
            window.onmouseup = null ;
            ctx2.restore();
            ctx1.restore();
            //绘制在画布1上
            shape.ctx = ctx1 ;
            if(shape.shapeType != 'rubber'){
                shape.draw();
            }
            ctx2.clearRect(0,0,canvas2.width , canvas2.height);
        }


    }


    
}